package offerbook;

/**
 * 请实现一个函数用来判断字符串是否表示数值（包括整数和小数）。
 * 例如，字符串"+100","5e2","-123",".123","+e3","3.1416E+10"和"-1E-16"都表示数值。
 * 但是"12e","1a3.14","1.2.3","+-5"和"12e+4.3"都不是。
 * <p>
 * <p>
 * 分析：数值的形式是：
 * [符号位1]  0-9  [小数点与0-9[E/e[符号位]0-9]]   [E/e[符号位]0-9]
 * <p>
 * 符号位1可有可无
 * 小数点可有可无，如果有后面必须是0-9，然后判断后面是否是科学计数
 * 科学计数可有可无：如果是科学计数, 满足： e前面必须有数字，e后面可以有+-，必须有数字
 * <p>
 * <p>
 * 方式2：使用正则表达式，但是注意导包 java.util.regex.Pattern
 *
 * @date 2020/3/12 17:12
 */
public class Code54_InvalidNum_有效数字 {
    public static boolean isNumeric1(char[] chars) {
        if (chars == null || chars.length == 0) return false;
        int index = 0;
        //1先判断是否有符号位
        if (chars[0] == '+' || chars[0] == '-') {
            if (chars.length == 1) return false;
            index++;
        }
        //2符号位过后，扫描第一个非 0-9的数字的索引
        index = scanDigits(chars, index);
        //全是数字的情况：
        if (index >= chars.length) return true;
        //有小数点的情况:注意如果下一位是小数点，那么0可以省略 -.3
        if (chars[index] == '.') {
            return isSmallPoint(chars, ++index);
        }
        //有E或e的情况:注意如果下一位是e或E，e前面必须有至少一位数字,1也不可以省略，是为了区分
        //到底是科学技术还是单纯的e
        if (chars[index] == 'e' || chars[index] == 'E') {
            if(!(chars[index-1] >= '0' && chars[index-1] <= '9')) return false;
            return isExp(chars, ++index);
        }
        //其他非法字符
        return false;

    }

    private static boolean isExp(char[] chars, int index) {
        //e或E直接是最后字符，为false
        if(index >= chars.length) return false;
        //e或E后面有+ -
        if (chars[index] == '+' || chars[index] == '-') {
            //是否以-+结尾
            if(index >= chars.length) return false;
            index++;
        }
        if (index == chars.length) return false;
        //扫描非数字，必须要求后面全是数字
        index = scanDigits(chars, index);
        return index >= chars.length;
    }

    /**
     * 判断是否是小数点形式 ，里面包含着科学计数
     */
    private static boolean isSmallPoint(char[] str, int index) {
        if (index == str.length) return false;
        //扫描找到第一个非数字的位置
        index = scanDigits(str, index);
        if (index >= str.length) return true;
        //是否是e或E，不是则返回false
        if (str[index] == 'e' || str[index] == 'E') {
            if(!(str[index-1] >= '0' && str[index-1] <= '9')) return false;
            return isExp(str, ++index);
        }
        //其他非法字符
        return false;
    }

    /**
     * 从str的index开始扫描数字，返回值是第一个不为数字的index
     */
    private static int scanDigits(char[] str, int index) {
        while (index < str.length && str[index] >= '0' && str[index] <= '9') index++;
        return index;
    }

    public static boolean isNumeric2(char[] chars){
        if(chars == null || chars.length == 0) return false;
        // +代表至少1次， ？代表0次或1次。 \\+表示 +   .表示任意字符, \\.表示.

        /**
         * 分析：1、首先+-最多出现1次 [\\+-]?
         * 2、是否有数字  \\d*表示任意次
         * 3、是否有小数点 \\. ?
         * 4、没有小数点必须有数字，有小数点也必须有数字 \\d+
         * 5、是否包含 e的表达式，e前面必须有数字，后面可以出现+-,但是必须有数字
         *  (\\d+[eE][\\+-]?\\d+)?
         *
         *  4和5的\\d+可以合并
         *
         *  最后结果  ^[\+-]?\d*\.?\d+([eE[\+-]?]\d+)?$
         */
        String pattern = "^[\\+-]?\\d*\\.?\\d+([eE][\\+-]?\\d+)?$";
        return new String(chars).matches(pattern);
    }
    public static void main(String[] args) {
        String str = "-.1e+3";

        boolean numeric = isNumeric1(str.toCharArray());
        boolean numeric1 = isNumeric2(str.toCharArray());

        System.out.println("numeric = " + numeric);
        System.out.println("numeric = " + numeric1);
    }
}
